<template>
  <div id="amap-3d"></div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted } from "vue"
import AMapLoader from "@amap/amap-jsapi-loader"
import * as THREE from 'three'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
import vertShader from '@/shaders/A.vert';
import fragShader from '@/shaders/A.frag';
import { getSHGeoJson } from '@/apis/geoMap'

const emit=defineEmits(['initLayer','renderLayer'])
let map:any = null;
const AMAP_KEY=import.meta.env.VITE_AMAP_KEY
const shGeo:any=await getSHGeoJson();
onMounted(() => {
  AMapLoader.load({
    key: AMAP_KEY,
    version: "2.0",
    plugins: [],
  }).then((AMap) => {
    map = new AMap.Map("amap-3d", {
      //center: [116.54, 39.79],
      center:[121.500419, 31.238089],
      zooms: [2, 20],
      zoom: 15,
      viewMode: '3D',
      pitch: 50,
      mapStyle: 'amap://styles/grey',
      showBuildingBlock: false,
      showLabel: false,
    })
    createGlLayer(AMap);
    updateRender();
  })
})
const createGlLayer=(AMap:any) =>{
  const gllayer = new AMap.GLCustomLayer({
    zIndex: 15,
    init: initGlLayer,
    render: renderGlLayer,
  });
  map.add(gllayer);
}
var camera:any;
var renderer:any;
var scene:any;
var width:number,height:number;
const initGlLayer=async (gl:any) =>{
  initScene(gl);
  buildFloor();
}
const renderGlLayer=() =>{
  renderer.resetState();
  var customCoords = map.customCoords;
  customCoords.setCenter([121.500419, 31.238089]);
  var { near, far, fov, up, lookAt, position } =
    customCoords.getCameraParams();
  camera.near = near;
  camera.far = far;
  camera.fov = fov;
  camera.position.set(...position);
  camera.up.set(...up);
  camera.lookAt(...lookAt);
  camera.updateProjectionMatrix();
  renderer.render(scene, camera);
  renderer.resetState();
}
const initScene=(gl:any) =>{
  width=window.innerWidth;
  height=window.innerHeight;
  camera = new THREE.PerspectiveCamera(
    60,
    width / height,
    100,
    1 << 30
  );
  renderer = new THREE.WebGLRenderer({
    context: gl,
    // alpha: true,
    // antialias: true,
    // canvas: gl.canvas,
  });
  renderer.autoClear = false;
  renderer.setPixelRatio( window.devicePixelRatio );
  scene = new THREE.Scene();
  // scene.fog = new THREE.Fog('lightblue', 20, 500);
  // scene.background = new THREE.Color('lightblue');
  var aLight = new THREE.AmbientLight(0xffffff, 0.3);
  var dLight = new THREE.DirectionalLight(0xffffff, 1);
  dLight.position.set(1000, -100, 900);
  scene.add(dLight);
  scene.add(aLight);
}
const generateTopUV=(geometry:any,vertices:number[],idxA:number,idxB:number,idxC:number):any[] =>{
  const uvs:any[]=[];
  // const x1=vertices[idxA*3],y1=vertices[idxA*3+1],z1=vertices[idxA*3+2];
  // const x2=vertices[idxB*3],y2=vertices[idxB*3+1],z2=vertices[idxB*3+2];
  // const x3=vertices[idxC*3],y3=vertices[idxC*3+1],z3=vertices[idxC*3+2];
  uvs.push({ x:0, y:0});
  uvs.push({ x:0, y:0});
  uvs.push({ x:0, y:0});
  //console.log(geometry.userData,uvs,[x1,y1,z1],[x2,y2,z2],[x3,y3,z3]);
  return uvs;
}
const generateSideWallUV=(geometry:any,vertices:number[],idxA:number,idxB:number,idxC:number,idxD:number):any[] =>{
  const uvs:any[]=[];
  // const x1=vertices[idxA*3],y1=vertices[idxA*3+1],z1=vertices[idxA*3+2];
  // const x2=vertices[idxB*3],y2=vertices[idxB*3+1],z2=vertices[idxB*3+2];
  // const x3=vertices[idxC*3],y3=vertices[idxC*3+1],z3=vertices[idxC*3+2];
  // const x4=vertices[idxD*3],y4=vertices[idxD*3+1],z4=vertices[idxD*3+2];
  uvs.push({ x:0, y:0});
  uvs.push({ x:1, y:0});
  uvs.push({ x:1, y:1});
  uvs.push({ x:0, y:1});
  //console.log(uvs,dx,dy,[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],[x4,y4,z4]);
  return uvs;
}
const uniforms={
  uMinHeight:{
    value: 0
  },
  uMaxHeight:{
    value: 0
  },
  uTime: {
    value: 0
  },
  uDirectionalColor: { 
    value: new THREE.Color(1.0, 1.0, 1.0)
  },
  uDirection: {
    value: new THREE.Vector3(-0.7, 0.0, 0.2)
  }
}
const createShaderMaterial=() =>{
  //return new THREE.MeshStandardMaterial( { color:0x44aa88 } );
  const mat=new THREE.ShaderMaterial({
    uniforms,
    vertexShader: vertShader,
    fragmentShader: fragShader,
    transparent:true
  });
  return mat;
}
const buildFloor=() =>{
  var customCoords = map.customCoords;
  //shGeo.features=[shGeo?.features[0]];
  const geometrys=[];
  const [x,y] = customCoords.lngLatToCoord([121.500419, 31.238089]);
  for(let v of shGeo?.features){
    const polygons=v.geometry.coordinates[0];
    const height=v.properties.h;
    if(uniforms.uMinHeight.value>height){
      uniforms.uMinHeight.value=height;
    }
    if(uniforms.uMaxHeight.value<height){
      uniforms.uMaxHeight.value=height;
    }
    const shape = new THREE.Shape();
    polygons.forEach((v1:any,i1:number) =>{
      const [x,y]=customCoords.lngLatToCoord(v1);
      if(i1==0){
        shape.moveTo( x,y );
      }else{
        shape.lineTo( x,y );
      }
    })
    const geometry = new THREE.ExtrudeGeometry( shape, {
      steps: 1,
      depth: height,
      bevelEnabled: false,
      UVGenerator:{
        generateTopUV,
        generateSideWallUV
      }
    });
    geometrys.push(geometry)
  }
  const mergedGeometry=BufferGeometryUtils.mergeGeometries(geometrys,false);
  const material=createShaderMaterial();
  const mesh = new THREE.Mesh(mergedGeometry, material);
  scene.add( mesh );
  const axesHelper = new THREE.AxesHelper( 500 );
  //scene.add( axesHelper );
  //console.log(mergedGeometry);
}
const updateRender=() =>{
  if(uniforms.uTime.value<uniforms.uMaxHeight.value){
    uniforms.uTime.value +=1;
    const zVector=new THREE.Vector3(0, 0, 1);
    uniforms.uDirection.value.applyAxisAngle(zVector,0.005);
  }else{
    uniforms.uTime.value =0;
  }
  
  map.render();
  requestAnimationFrame(updateRender);
}
function onWindowResize() {
  width=window.innerWidth;
  height=window.innerHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
}
window.addEventListener('resize', onWindowResize);
onUnmounted(() => {
  map?.destroy()
})
</script>

<style scoped lang="scss">
#amap-3d{
  width: 100vw;
  height: 100vh;
}
</style>